#include "cmatrixprod.h"
#include <libspe2.h>


typedef struct {
  abs_params_t *params;
  //spe_stop_info_t stop;
  spe_context_ptr_t ctx; 
} thread_block_t;


/* fonction generique extraite du tutorial IBM du SDK 3.0 pour lancer 
   les threads */

void *ppu_pthread_function(void *arg) {
 
  uint32_t entry = SPE_DEFAULT_ENTRY;
  thread_block_t *block=(thread_block_t *)(arg);
  //  block = *((thread_block_t *)arg);

  spe_context_ptr_t ctx=block->ctx;

  abs_params_t *abs_params=block->params; 
  
  if (spe_context_run(ctx, &entry, 0, abs_params, NULL, NULL) < 0) 
    perror ("Failed running context");
  
  pthread_exit(NULL);
}

//int spe_context_run(spe_context_ptr_t spe, unsigned int *entry, unsigned int runflags,
//
//                    void *argp, void *envp, spe_stop_info_t *stopinfo);

/* Cette fonction, appelé par le main, s'éxécute sur le PPE et va lancer des 
   programmes sur les SPEs */

int cmatrixprod(int M, int N, int P,t_complex *A, 
		t_complex *B,t_complex *C){

  /**********************Variables**************************/

  int ret,i,j;
  int nbspe,stop=50;
  unsigned int adrl,tmp;
  char *adr_in_A[NUM_SPE], *adr_in_B[NUM_SPE];
  spe_context_ptr_t context[NUM_SPE];
  pthread_t thread[NUM_SPE];
  thread_block_t arg[NUM_SPE];
  spe_program_handle_t *prog;
  //unsigned int entry;
  //spe_stop_info_t stop_info[NUM_SPE];
  
    
  abs_params_t abs_params __attribute__((aligned(16))); 

  /* préparation de la structure de paramètres */
  abs_params.size_M = M;
  abs_params.size_N = N;
  abs_params.size_P = P;
  abs_params.size_block = SIZE_BLOCK;
    
  /*on positionne le bloque traite par le spe*/
  abs_params.in_matrix_A = (long long)A;
  abs_params.in_matrix_B = (long long)B;
  abs_params.out_matrix_C = (long long)C;
  
  /**********************************************************/
 
  
  /***************** recuperation du nombre de SPE******************* */
  /*******************************************************************/

  nbspe = spe_cpu_info_get(SPE_COUNT_USABLE_SPES, -1);
  if (nbspe < NUM_SPE) 
    perror("pas assez de SPE");
  if (P%SIZE_BLOCK!=0)
    perror("la taille doit etre multiple du nombre de spe");

  
  prog = spe_image_open("cmatrixprod_spe");
  if (!prog) {
    perror("spe_image_open");
    exit(1);
  }
  

  for (i = 0; i < NUM_SPE; i++) {
    
  
    //printf("PPE: @ de in_A %llx pour %d \n",abs_params.in_matrix_A,i);
    //printf("taille %d\n",sizeof(abs_params_t));

    /***************** Creation des contextes d'execution ****************/
    /*********************************************************************/
    
    context[i] = spe_context_create(0, NULL);
    if (!context[i]) {
      perror("spe_context_create");
      exit(1);
    }
     
    /************ chargement du code que va executer le ieme SPE *****************/
    /*****************************************************************************/

    ret = spe_program_load(context[i], prog);
    if (ret) {
      perror("spe_program_load");
      exit(1);
    }
    
    arg[i].params = &(abs_params);
    //arg[i].stop = stop_info[i];
    arg[i].ctx = context[i];
   
    /*************************** creation d'un thread ****************************/
    /*****************************************************************************/

    if (pthread_create (&thread[i], NULL, &ppu_pthread_function, &arg[i]))  
      perror("creation thread");
    

    /***********************envoi d'un numero à chaque thread*********************/
    /*****************************************************************************/

    while(spe_in_mbox_status(context[i])==0);
    spe_in_mbox_write(context[i],&i,1,SPE_MBOX_ALL_BLOCKING);
  }
 
   
  for(i=0;i<NUM_SPE;i++){
    /************reception des adresses in_A du SPE i**********************/
    while (spe_out_mbox_status(context[i])==0);    
    spe_out_mbox_read(context[i], &adrl, 1);
    adr_in_A[i]=(char*)spe_ls_area_get(context[i])+adrl;
    //printf("PPE : adresse de in_A %d : %llx\n",i,(unsigned long long) adr_in_A[i]);
  }


  for(i=0;i<NUM_SPE;i++){
    /************reception des adresses in_B du SPE i**********************/
    while (spe_out_mbox_status(context[i])==0);
    spe_out_mbox_read(context[i], &adrl, 1);
    adr_in_B[i]=(char*)spe_ls_area_get(context[i])+adrl;
    //printf("PPE : adresse de in_B %d : %llx\n",i,(unsigned long long) adr_in_B[i]);
    
  }
  
  
  for(i=0;i<NUM_SPE;i++){
  
    /*envoi de  l'adresse EA de la variable in_A de SPE i a SPE i+1 %NUM_SPE*/
    tmp=((long long unsigned int)adr_in_A[i]>>32);  
    while (spe_in_mbox_status(context[(i+1)%NUM_SPE])==0);
    spe_in_mbox_write(context[(i+1)%NUM_SPE], &tmp, 1,SPE_MBOX_ALL_BLOCKING);
    tmp=(unsigned int) (unsigned long long) adr_in_A[i];
    while (spe_in_mbox_status(context[(i+1)%NUM_SPE])==0);
    spe_in_mbox_write(context[(i+1)%NUM_SPE], &tmp,1,SPE_MBOX_ALL_BLOCKING);
    
  }

  for(i=0;i<NUM_SPE;i++){
    /*envoi de  l'adresse EA de la variable in_B de SPE i a SPE i+1 %NUM_SPE*/
    tmp=((long long unsigned int)adr_in_B[i]>>32);  
    while (spe_in_mbox_status(context[(i+1)%NUM_SPE])==0);
    spe_in_mbox_write(context[(i+1)%NUM_SPE], &tmp, 1,SPE_MBOX_ALL_BLOCKING);
    tmp=(unsigned int) (unsigned long long) adr_in_B[i];
    while (spe_in_mbox_status(context[(i+1)%NUM_SPE])==0);
    spe_in_mbox_write(context[(i+1)%NUM_SPE], &tmp,1,SPE_MBOX_ALL_BLOCKING);
    //printf("PPE : adresse 64 de %d in_B envoyé a %d\n",i,i+1);
  }
    
  while(stop!=-1){
    /*reception des notifications de fin de chargement pour tous les spe*/
    for(i=0;i<NUM_SPE;i++){
      int aux=stop;
      while (spe_out_mbox_status(context[i])==0);
      spe_out_mbox_read(context[i], &stop, 1);
      if((i>0)&&(aux!=stop)) perror("croisement de signal");
    }
    
    //printf("PPE: receptions Termines\n");
    
    /*envoi des autorisations de chargement du voisins aux spe*/
    for(i=0;i<NUM_SPE;i++){
      while (spe_in_mbox_status(context[i])==0);
      spe_in_mbox_write(context[i], &i, 1,SPE_MBOX_ALL_BLOCKING);
    }
    //printf("PPE: Autorisations envoyes\n");
  }

  printf("PPE : fin des threads\n");
  
  
  /* attente de la fin d'execution  */
  for (i=0; i<NUM_SPE; i++) { 
    /* on verifie la fin du thread */
     if (pthread_join (thread[i], NULL)) 
       perror("probleme pthread_join");
     
     /* Destruction du contexte */
     if (spe_context_destroy (context[i]) != 0) 
       perror("probleme destroying context");
  }
  
  ret = spe_image_close(prog);
  if (ret) {
    perror("spe_image_close");
    exit(1);
  }
  
  return 0;
}
  
